#pragma once

#include <iostream>
#include "Socked.hpp"
#include <sys/time.h>

using namespace std;
static const uint16_t defaultport = 8080;
int defaultfd = -1;
class SelectServer
{
public:
    SelectServer(const uint16_t port = defaultport)
        : _port(port)
    {
        for (int i = 0; i <= 1024; i++)
        {
            _fd_array[i] = -1;
            printf("fd_array[%d]: %d\n", i, _fd_array[i]);
        }
    }
    ~SelectServer()
    {
        _listensock.Close();
    }

    bool Init()
    {
        _listensock.Socket(); // 创建套接字
        _listensock.Bind(_port);
        _listensock.Listen();

        return true;
    }

    void HandlerEvent(fd_set &rfds)
    {
        for (int i = 0; i < 1024; i++)
        {
            int fd = _fd_array[i];
            if (fd == -1)
                continue;
            if (FD_ISSET(fd, &rfds)) // 资源已就绪
            {
                if (fd == _listensock.Fd()) // 监听套接字就绪
                {
                    string clientip;
                    uint16_t clientport = 0;
                    int sock = _listensock.Accept(&clientip, &clientport); // 这一次连接，不会阻塞
                    if (sock < 0)
                    {
                        lg(Error, "accepet error");
                        return;
                    }
                    lg(Info, "accept success, %s: %d, fd: %d", clientip.c_str(), clientport, sock);

                    // 不能直接进行read、write
                    // 原因举例read，此时的缓冲区可能没有数据，导致read阻塞在这里，进而导致HandlerEvent函数无法返回
                    // 所以资源是否就绪的问题，统统交给上面的select函数处理（三个位图参数）

                    // 正解:将获取的新连接sock交给select等待
                    // 将 sock 添加到辅助数组即可，因为辅助数组会在while中不断刷新，并且交给select
                    // for (int i = 1; i <= 1024; i++) // 0已被listensock占用
                    // {
                    //     if (_fd_array[i] != -1)
                    //     {
                    //         if(i == 1024)
                    //         {
                    //             // 没有空位了
                    //             lg(Warning, "Server is Full, already close fd: %d\n", sock);
                    //             close(sock);
                    //             break;
                    //         }
                    //         continue;
                    //     }
                    //     else{
                    //         _fd_array[i] = sock;
                    //         lg(Info, "已转接sock: %d 至辅助数组\n", sock);
                    //         break;
                    //     }
                    // }

                    int pos = 1;
                    for (; pos < 1024; pos++) // 第二个循环
                    {
                        if (_fd_array[pos] != -1)
                            continue;
                        else
                            break;
                    }
                    if (pos == 1024)
                    {
                        lg(Warning, "server is full, close %d now!", sock);
                        close(sock);
                    }
                    else
                    {
                        _fd_array[pos] = sock;
                    }
                }
                else // 说明执行的是其余文件描述符资源
                {
                    char buffer[1024];
                    ssize_t n = read(fd, &buffer, sizeof(buffer) - 1);
                    if (n > 0)
                    {
                        buffer[n] = 0;
                        cout << "get a message: " << buffer << endl;
                    }
                    else if (n == 0)
                    {
                        // 对方关闭文件描述符
                        lg(Info, "client quit, me too, fd is: %d", fd);
                        close(fd);
                        _fd_array[i] = -1;
                    }
                    else
                    {
                        lg(Warning, "read error\n");
                        close(fd);
                        _fd_array[i] = -1;
                    }
                }
            }
        }
    }
    void Start()
    {
        int listensock = _listensock.Fd();
        _fd_array[0] = listensock;
        while (1)
        {
            fd_set rfds;
            FD_ZERO(&rfds);
            int maxfd = _fd_array[0];
            for (int i = 0; i < 1024; i++)
            {
                if (_fd_array[i] == -1)
                {
                    continue;
                }

                FD_SET(_fd_array[i], &rfds); // 将辅助数组中所有的fd进行等待
                if (maxfd < _fd_array[i])
                    maxfd = _fd_array[i]; // 找到这个辅助数组中最大的fd
                lg(Info, "maxfd update: %d", maxfd);
            }

            // fd_set rfds;               // 定义【等待读资源就绪位图】
            //                         //  rfds输入输出型参数1111    0001
            // FD_ZERO(&rfds);            // 先清零
            // FD_SET(listensock, &rfds); // 此时并未设置在内核中， 因为rfds是栈上的一个变量（用户空间）

            struct timeval timeout = {2, 0};                              // 设置为5s，每次查询时 阻塞5s后返回【若在循环体外设置 意味着只有第一次等待5秒，后续全为轮询】
            int n = select(maxfd + 1, &rfds, nullptr, nullptr, &timeout); // timeout【null:阻塞， {0, 0}非阻塞】

            switch (n) // 对select返回值进行判断
            {
            case 0: // 超时
                cout << "time out: " << timeout.tv_sec << "." << timeout.tv_usec << endl;
                break;
            case -1: // 错误
                cerr << "select error" << endl;
                break;
            default:                               // 有事件就绪
                cout << "get a new link!" << endl; // 这里会刷屏【因为select需要等待将该连接取走，不取走 select会一直通知你，handlerEvent处理】
                HandlerEvent(rfds);
                break;
            }
        }
    }

private:
    Sock _listensock;
    uint16_t _port;

    // selete最大特点之一：允许文件描述符表在不同的函数之间进行传递【辅助数组】
    int _fd_array[1024];
};